/**
 * Generali Solutions d'assurances - Tous droits rï¿½servï¿½s &copy; 2007 - 2010
 */
package fr.generali.accueilclient.commun.rce;

import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.helpers.DefaultValidationEventHandler;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.collections.Transformer;
import org.apache.commons.lang.StringUtils;
import org.junit.Assert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.core.io.ClassPathResource;

/**
 * @author Holmes Kelly
 */
public abstract class AbstractBcRceUnitilsMockTestCaseHelper {

    /**
     * 
     */
    protected static Logger log = LoggerFactory.getLogger(AbstractBcRceUnitilsMockTestCaseHelper.class);

    /**
     * @param objectExpect l'objet attendu
     * @param objectTest l'objet test
     * @throws Exception exception has occurred
     */
    protected void compareBeanFields(Object objectExpect, Object objectTest) throws Exception {

        BeanWrapper beanWrapperExpect = new BeanWrapperImpl(objectExpect);

        BeanWrapper beanWrapperTest = new BeanWrapperImpl(objectTest);

        PropertyDescriptor[] propertyDescriptors = beanWrapperExpect.getPropertyDescriptors();

        Map<String, Class< ? >> mapTypeSemiPrimitifs = getMapTypeSemiPrimitifs();

        Map<String, Class< ? >> mapTypeList = getMapTypeList();

        for (PropertyDescriptor propertyDescriptorLoc : propertyDescriptors) {

            Object propertyValueExpect = beanWrapperExpect.getPropertyValue(propertyDescriptorLoc.getName());
            Object propertyValueTest = beanWrapperTest.getPropertyValue(propertyDescriptorLoc.getName());

            if (mapTypeSemiPrimitifs.values().contains(propertyDescriptorLoc.getPropertyType())) {

                Assert.assertEquals(propertyValueExpect, propertyValueTest);
            }

            if (mapTypeList.values().contains(propertyDescriptorLoc.getPropertyType())) {

                if (propertyValueExpect != null && propertyValueTest != null) {
                    compareBeans(propertyValueExpect, propertyValueTest);
                }
            }
        }
    }

    /**
     * @return mapType une map
     */
    protected Map<String, Class< ? >> getMapTypeSemiPrimitifs() {

        Map<String, Class< ? >> mapType = new HashMap<String, Class< ? >>();

        MapUtils.safeAddToMap(mapType, String.class.getName(), String.class);
        MapUtils.safeAddToMap(mapType, BigInteger.class.getName(), BigInteger.class);
        MapUtils.safeAddToMap(mapType, BigDecimal.class.getName(), BigDecimal.class);
        MapUtils.safeAddToMap(mapType, XMLGregorianCalendar.class.getName(), XMLGregorianCalendar.class);
        MapUtils.safeAddToMap(mapType, Date.class.getName(), Date.class);

        return mapType;

    }

    /**
     * @return mapType une map
     */
    protected Map<String, Class< ? >> getMapTypeList() {

        Map<String, Class< ? >> mapType = new HashMap<String, Class< ? >>();

        MapUtils.safeAddToMap(mapType, List.class.getName(), List.class);
        MapUtils.safeAddToMap(mapType, Map.class.getName(), Map.class);
        return mapType;

    }

    /**
     * @param objectExpect expect object
     * @param objectTest test object
     * @throws Exception exception
     */
    protected void compareBeans(Object objectExpect, Object objectTest) throws Exception {

        if (objectExpect instanceof Collection< ? > && objectTest instanceof Collection< ? >) {

            Iterator< ? > iteratorExpect = ((Collection< ? > ) objectExpect).iterator();

            Iterator< ? > iteratorTest = ((Collection< ? > ) objectTest).iterator();

            while (iteratorExpect.hasNext() && iteratorTest.hasNext()) {

                Object itemExpect = iteratorExpect.next();

                Object itemTest = iteratorTest.next();

                compareBeanFields(itemExpect, itemTest);

            }
        } else if (objectExpect instanceof Map< ? , ? > && objectTest instanceof Map< ? , ? >) {

            Map< ? , ? > mapExpect = (Map< ? , ? > ) objectExpect;
            Set< ? > keySetExpect = mapExpect.keySet();
            Map< ? , ? > mapTest = (Map< ? , ? > ) objectTest;
            Set< ? > keySetTest = mapTest.keySet();

            if (mapExpect.size() == mapTest.size() && keySetExpect.size() == keySetTest.size()) {

                for (Object keyMap : keySetExpect) {
                    Object itemExpect = mapExpect.get(keyMap);
                    Object itemTest = mapTest.get(keyMap);
                    compareBeanFields(itemExpect, itemTest);
                }

            }
        }

        else {

            compareBeanFields(objectExpect, objectTest);
        }

    }

    /**
     * @param classBean class
     * @return transformer
     */
    protected Transformer getTransformerBean(Class< ? > classBean) {

        final Class< ? > classBeanTrans = classBean;

        Transformer transformer = new Transformer() {

            public Object transform(Object input) {

                return classBeanTrans.cast(input);
            }
        };

        return transformer;
    }

    /**
     * @param <T> le type de la classe concernï¿½e
     * @param fileName nom fichier
     * @param clazz la classe concernï¿½e
     * @return ConsulterLibelleResponse
     */
    protected <T> T getXMLToObject(String fileName, Class<T> clazz) {

        String baseClassPath = null;
        JAXBContext jaxbContext = null;

        // Objet JAXB representant la reponse retournee par le Web Service
        // apres l'unmarshalisation
        JAXBElement<T> wsResponse = null;

        T responseOutput = null;
        // Defintion du chemin du fichier XML
        // baseClassPath =
        // "com/sfr/composants/psgws/fasiconnector/mockreponse/recupdonneesperso/";
        baseClassPath = StringUtils.EMPTY;

        log.debug("Chemin du fichier XML : " + baseClassPath);

        ClassLoader classloader = Thread.currentThread().getContextClassLoader();
        String path = (new StringBuilder()).append(baseClassPath).append(fileName).append(".xml").toString();
        ClassPathResource xmlClientResource = new ClassPathResource(path, classloader);

        log.debug("Nom du fichier XML trouve : " + path);

        InputStream xmlFileAsStream;
        try {
            xmlFileAsStream = xmlClientResource.getInputStream();

            // Unmarshall du fichier XML avec JAXB
            jaxbContext = JAXBContext.newInstance(clazz);
            jaxbContext.createUnmarshaller();
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
            unmarshaller.setEventHandler(new DefaultValidationEventHandler());
            wsResponse = unmarshaller.unmarshal(new StreamSource(xmlFileAsStream), clazz);
            responseOutput = (T ) wsResponse.getValue();

            log.info(responseOutput.toString());

        } catch (IOException ioe) {
            log.error("Pas de fichier " + xmlClientResource + " trouve - " + ioe);
            return null;
        } catch (JAXBException jaxe) {
            throw new RuntimeException((new StringBuilder()).append("can NOT build Response for fileName '")
                            .append(fileName).append("'").toString(), jaxe);
        }
        return responseOutput;
    }
}
